#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/kmod.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/uaccess.h>

#define DHT11_EDGES_PREAMBLE    2
#define DHT11_BITS_PER_READ     40

#define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \
                  DHT11_EDGES_PREAMBLE + 1)

#define DHT11_THRESHOLD     49000  /* ns */


struct dht11_struct{
    struct gpio_desc *gpio;
    int irq;
    int num_edges;
    int humidity;
    int temperature;
    struct {
        u64 ts;
        int value;
    } edges[DHT11_EDGES_PER_READ+5];
};

static struct dht11_struct *sensor_dht11;

/* 1-确定主设备号                                                                 */
static int major = 0;
static int complete_flag = 0;
static struct class *dht11_class;


static DECLARE_WAIT_QUEUE_HEAD(dht11_waitqueue);

static irqreturn_t xxh_sensor_dht11_isr(int irq, void *dev_id);


static unsigned char dht11_decode_byte(char *bits)
{
    unsigned char ret = 0;
    int i;

    for (i = 0; i < 8; ++i) {
        ret <<= 1;
        if (bits[i])
            ++ret;
    }

    return ret;
}

static int dht11_decode(struct dht11_struct *dht11, int offset)
{
    int i, t;
    unsigned char hum_int, hum_dec, tem_int, tem_dec, checksum;
    char bits[DHT11_BITS_PER_READ];

    for (i = 0; i<DHT11_BITS_PER_READ; i++)
    {
        t = dht11->edges[offset + i*2 + 2].ts -
            dht11->edges[offset + i*2 + 1].ts;

        if(!dht11->edges[offset + i*2 + 1].value)
        {
            pr_err("lost synchronisation at edge %d\n", offset+i*2+1);
            return -EIO;
        }

        bits[i] = t > DHT11_THRESHOLD ? 1 : 0;
    }

    hum_int = dht11_decode_byte(bits);
    hum_dec = dht11_decode_byte(&bits[8]);
    tem_int = dht11_decode_byte(&bits[16]);
    tem_dec = dht11_decode_byte(&bits[24]);
    checksum = dht11_decode_byte(&bits[32]);

    if(((hum_int + hum_dec + tem_int + tem_dec) & 0xFF) != checksum)
    {
        pr_err("invalid checksum!!!\n");
        return -EINVAL;
    }

    dht11->humidity = hum_int*100 + hum_dec;
    dht11->temperature = tem_int*100 + tem_dec;

    return 0;
}


static int sensor_dht11_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int len, l_offset;
    int err, ret;
    int data[3];

    printk("%s line %d: Start read DHT11 sensor.\n", __FUNCTION__, __LINE__);

    len = (size < 8) ? 8 : size;
    sensor_dht11->num_edges = 0;

    /* start条件: 输出方向 */
    gpiod_direction_output(sensor_dht11->gpio, 1);
    mdelay(20); // 延时20ms
    gpiod_set_value(sensor_dht11->gpio, 0);
    mdelay(20); // 延时20ms
    gpiod_set_value(sensor_dht11->gpio, 1);
    udelay(40); // 延时40us

    /* 开始接收数据: 输入方向 */
    err = gpiod_direction_input(sensor_dht11->gpio);
    /* 申请中断 */
    err = request_irq(sensor_dht11->irq, xxh_sensor_dht11_isr, 
                      IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, 
                      "xxh_sensor_dht11", NULL);
    //printk("%s line %d: GPIO changes to the input direction.\n", __FUNCTION__, __LINE__);

    /* 等待事件条件为真 */
    ret = wait_event_interruptible_timeout(dht11_waitqueue, complete_flag, HZ);
    free_irq(sensor_dht11->irq, NULL);
    if(ret <= 0)
    {
        pr_err("event timeout 1s, condition = %d, dht11 count edges = %d !!!\n", complete_flag, sensor_dht11->num_edges);
        return -ETIMEDOUT;
    }

    complete_flag = 0;
    gpiod_direction_output(sensor_dht11->gpio, 1);    //改为输入出方向-230804 /* 改回输入方向-230803 */
    
    if(sensor_dht11->num_edges < DHT11_EDGES_PER_READ)
    {
        pr_err("count number of data edges less than 84 !!!\n");
        return -EAGAIN;
    }
    
    l_offset = DHT11_EDGES_PREAMBLE + sensor_dht11->num_edges - DHT11_EDGES_PER_READ;
    printk("decode offset = %d\n", l_offset);
    err = dht11_decode(sensor_dht11, l_offset);
    if(err = 0) {
        data[0] = sensor_dht11->humidity;
        data[1] = sensor_dht11->temperature;
        err = copy_to_user(buf, &data, len);
        return len; 
    }
    else {
        return -EAGAIN;
    }

}


/* 按键读取驱动 file_operations */
static struct file_operations xxh_dht11_drv = {
    .owner = THIS_MODULE,
    .read  = sensor_dht11_drv_read,
};

/* 中断服务函数 */
static irqreturn_t xxh_sensor_dht11_isr(int irq, void *dev_id)
{
    //struct dht11_struct *dht11 = dev_id;

    if(sensor_dht11->num_edges < DHT11_EDGES_PER_READ && sensor_dht11->num_edges>=0)
    {
        sensor_dht11->edges[sensor_dht11->num_edges].ts = ktime_get_boot_ns();
        sensor_dht11->edges[sensor_dht11->num_edges++].value = gpiod_get_value(sensor_dht11->gpio);
        //printk("%s line %d: data = %d.\n", __FUNCTION__, __LINE__, sensor_dht11->edges[sensor_dht11->num_edges-1].value);
        
        if(sensor_dht11->num_edges >= DHT11_EDGES_PER_READ) {
            printk("%s line %d: wake up queue.\n", __FUNCTION__, __LINE__);
            complete_flag = 1;
            wake_up_interruptible(&dht11_waitqueue);
        }
    }

    return IRQ_HANDLED;
}


/*
 * 4、从platform device获取GPIO
 *    把file_operations结构体告诉内核：注册驱动程序
 */
static int xxh_sensor_dht11_probe(struct platform_device *pdev)
{
    int err;

    printk("%s line %d: probe DHT11 sensor.\n", __FUNCTION__, __LINE__);


    sensor_dht11 = kzalloc(sizeof(struct dht11_struct), GFP_KERNEL);
    if(sensor_dht11 == NULL)
        return -1;

    /* 设备节点获取 */
    sensor_dht11->gpio = gpiod_get(&pdev->dev, "dht11", GPIOD_OUT_HIGH);

    /* 获取GPIO中断号 */
    sensor_dht11->irq = gpiod_to_irq(sensor_dht11->gpio);
    if (sensor_dht11->irq < 0) {
        err = sensor_dht11->irq;
        dev_err(&pdev->dev,
            "Unable to get irq number for GPIO %d, error %d\n",
            desc_to_gpio(sensor_dht11->gpio), err);
        return err;
    }
    else {
        printk("%s line %d: get dht11 gpio irq number = %d\n", __FUNCTION__, __LINE__, sensor_dht11->irq);
    }
    

    /* 注册file_operations结构体 */
    major = register_chrdev(0, "xxh_dht11", &xxh_dht11_drv);
    dht11_class = class_create(THIS_MODULE, "xxhdht11");  /* /sys/class/xxhkey */
    err = PTR_ERR(dht11_class);
    if (IS_ERR(dht11_class)) {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "xxhdht11");
        return -1;
    }

    device_create(dht11_class, NULL, MKDEV(major, 0), NULL, "xxhdht11"); /* /dev/xxhkey */
    
    return 0;
}

static int xxh_sensor_dht11_remove(struct platform_device *dev)
{
    // struct device_node *np;
    // int index, count;
    // int ret;
    
    printk("%s line %d\n", __FUNCTION__, __LINE__);
    device_destroy(dht11_class, MKDEV(major, 0));
    class_destroy(dht11_class);
    unregister_chrdev(major, "xxh_dht11");

    /* 设备节点释放 */
    //free_irq(sensor_dht11->irq, &sensor_dht11);
    gpiod_put(sensor_dht11->gpio);
    kfree(sensor_dht11);
    
    return 0;
}


static const struct of_device_id sensor_dht11_dev[] = {
    {.compatible = "xxh,sensor_dht11"},
    {},
};

/* 1、定义platform_drver结构体 */
static struct platform_driver xxh_sensor_dht11_driver = {
    .probe  = xxh_sensor_dht11_probe,
    .remove = xxh_sensor_dht11_remove,
    .driver = {
        .name   = "xxh_sensor_dht11",
        .of_match_table = sensor_dht11_dev,
    },
};


static int __init sensor_dht11_init(void)
{
    int err;
    printk("%s line %d\n", __FUNCTION__, __LINE__);
    err = platform_driver_register(&xxh_sensor_dht11_driver);

    return err;
}


static void __exit sensor_dht11_exit(void)
{
    printk("%s line %d\n", __FUNCTION__, __LINE__);
    platform_driver_unregister(&xxh_sensor_dht11_driver);
    return;
}

/* 7. 其他完善：提供设备信息，自动创建设备节点                                     */

module_init(sensor_dht11_init);
module_exit(sensor_dht11_exit);

MODULE_AUTHOR("xxh <1293793140@qq.com>");
MODULE_LICENSE("GPL");

